/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.model;

import offset.nodes.client.virtual.model.SchemaItem;
import offset.nodes.client.virtual.model.SchemaNode;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import offset.nodes.client.virtual.model.jcr.QName;
import offset.nodes.client.virtual.model.jcr.nodetype.NodeTypeDefinition;

/**
 * This class provides some methods to manage schema tree representations, e.g. flat and hierarchical
 * tree. 
 * 
 * It provides methods to traverse the tree and creates abstract methods, that must be implemented 
 * in the specific tree representations.
 * @author Walter Luetgenau
 */
public abstract class AbstractSchemaTree {
    
    /** Creates a new instance of AbstractMutableTree */
    public AbstractSchemaTree() {
    }

    private DefaultMutableTreeNode root;

    private JTree tree;

    
    class ChildIterator {
        DefaultMutableTreeNode node;
        int current = 0;
        
        public ChildIterator(DefaultMutableTreeNode node) {
            this.node = node;
        }
        
        public DefaultMutableTreeNode next() {
            if ( current >= node.getChildCount() )
                return null;
            return (DefaultMutableTreeNode) node.getChildAt(current);
        }
        
        public boolean hasMore() {
            return current < node.getChildCount();
        }
        
        public void incrementIndex() {
            current++;
        }
    }

    
    interface Processor {
        public void process(DefaultMutableTreeNode node, offset.nodes.client.model.HierarchyTree.ChildIterator iterator);
    }

    /**
     * Extend a type tree node by adding a child. The user object of the child may be either a Schema Node
     * or a SchemaProperty.
     * 
     * If necessary, the child will be inserted everywhere in the tree, where a representation of the type 
     * is present.
     * @param parent The tree node representing the type
     * @param child The tree node representing the child
     * @param position The position, where the child shall be inserted
     */
    public abstract void addChildToType(DefaultMutableTreeNode parent, final DefaultMutableTreeNode child, final int position);

    /**
     * Add a tree node representing a type to the tree.
     * @param type The tree node representing the new type
     * @param position The position, the new type will be inserted. This is for current user interface only
     * and will currently not be persisted.
     */
    public abstract void addType(DefaultMutableTreeNode type, int position);

    
    /**
     * Process all children of the given node using the processor.
     * @param node The root node for the processing
     * @param processor The processor, that shall be applied to the children of the root node
     */
    public void processNode(DefaultMutableTreeNode node, offset.nodes.client.model.HierarchyTree.Processor processor) {
        ChildIterator iterator = new ChildIterator(node);
        while (iterator.hasMore()) {
            DefaultMutableTreeNode child = (DefaultMutableTreeNode) iterator.next();
            
            processNode(child, processor); // process children of child
            processor.process(child, iterator); // process child
        }
    }

    /**
     * Remove a child from the tree nodes representing the type.
     * 
     * If necessary, all tree nodes of the specific tree will be removed, that represent the type.
     * @param parent The tree node representing the type
     * @param child The tree node representing the child to be removed
     */
    public abstract void removeChildFromType(DefaultMutableTreeNode parent, DefaultMutableTreeNode child);

    /**
     * Remove the tree nodes, that represent the type from the tree.
     * @param node A tree node, that represents the type
     */
    public abstract void removeType(DefaultMutableTreeNode node);
        
    /**
     * Is the primary type supplied one of the supertypes of the given tree node?
     * @param treeNode The tree node 
     * @param type The type
     * @return 
     */
    protected boolean hasType(DefaultMutableTreeNode treeNode, NodeTypeDefinition type) {
        if ( !(treeNode.getUserObject() instanceof SchemaNode) )
            return false;
        
        SchemaNode node = (SchemaNode) treeNode.getUserObject();
        return node.getNodeType().getName().equals( type.getName());
    }
    
    /**
     * Has the given tree node the given name?
     * @param treeNode The tree node supplied
     * @param name The name
     * @return 
     */
    protected boolean hasName(DefaultMutableTreeNode treeNode, QName name) {
        if ( !(treeNode.getUserObject() instanceof SchemaItem) )
            return false;
        
        SchemaItem node = (SchemaItem) treeNode.getUserObject();
        return node.getName().equals( name );
    }

    public DefaultMutableTreeNode getRoot() {
        return root;
    }

    public void setRoot(DefaultMutableTreeNode root) {
        this.root = root;
    }

    public JTree getTree() {
        return tree;
    }

    public void setTree(JTree tree) {
        this.tree = tree;
    }
    
}
